#include <framework/framework_i.h>

/* For the naive mapping strategy */
#define PADDR(kva) ((physaddr_t)(kva) - KBASE)
#define KADDR(pa)  (void *)((pa) + KBASE)

struct smbios_ep_s
{
	 char     anchor[4];
	 uint8_t  checksum;
	 uint8_t  length;
	 uint8_t  ver_major;
	 uint8_t  ver_minor;
	 uint16_t structure_size_max;
	 uint8_t  ep_revision;
	 char     __area[5];
	 char     inter_anchor[5];
	 uint8_t  inter_checksum;
	 uint16_t st_size;
	 uint32_t st_phys;
	 uint16_t st_count;
	 uint8_t  resivion_bcd;
} __attribute__((packed));

struct st_header_s
{
	 uint8_t  type;
	 uint8_t  length;
	 uint16_t handle;
} __attribute__((packed));

struct st_cpu_info_s
{
	 uint8_t  socket;
	 uint8_t  type;
	 uint8_t  family;
	 uint8_t  manufacturer;
	 uint64_t id;
	 uint8_t  version;
	 uint8_t  volatage;
	 uint16_t ex_clock;
	 uint16_t max_speed;
	 uint16_t cur_speed;
	 uint8_t  status;
	 uint8_t  upgrade;
	 uint16_t l1_handle;
	 uint16_t l2_handle;
	 uint16_t l3_handle;
	 uint8_t  serial_num;
	 uint8_t  asset_tag;
	 uint8_t  part_num;
	 uint8_t  core_count;
	 uint8_t  core_enabled;
	 uint8_t  thread_count;
	 uint16_t characteristics;
	 uint16_t family_2;
} __attribute__((packed));

static uint8_t
sum(uint8_t *addr, int len)
{
	 int i, sum;
	 sum = 0;
	 for(i = 0; i < len; i++)
		  sum += addr[i];
	 return sum;
}

static struct smbios_ep_s*
smbios_ep_search_segment(uint8_t *addr, int len, uint32_t align)
{
	 uintptr_t e, p;
	 struct smbios_ep_s *result = NULL;

	 e = (uintptr_t)(addr + len) & ~align;
	 p = (uintptr_t)addr;
	 if (p & align) p = (p | align) + 1;
	 for(; p < e; p = p + align + 1)
		  if(memcmp((char *)p, "_SM_", 4) == 0 &&
			 sum((char *)p, 31) == 0 &&
			 memcmp((char *)p + 16, "_DMI_", 5) == 0 &&
			 sum((char *)p + 16, 15) == 0)
		  {
			   result = (struct smbios_ep_s*)p;
			   return result;
		  }
	 return NULL;
}

#define BDA 0x400

// Search for the RSDP, which according to the
// spec is in one of the following three locations:
// 1) in the first KB of the EBDA;
// 2) in the BIOS ROM between 0xE0000 and 0xFFFFF.
static struct smbios_ep_s*
smbios_ep_search(void)
{
	 return smbios_ep_search_segment((uint8_t *)KADDR(0xF0000), 0x10000, 0xf);
}

int
smbios_init(void)
{
	 struct smbios_ep_s *ep = smbios_ep_search();
	 if (!ep)
	 {
		  kprintf("NO SMBOIS IN MEMORY\n");
		  return -E_UNSUPPORTED;
	 }
	 else
	 {
	 }

	 int i;
	 struct st_header_s *header = (struct st_header_s *)KADDR(ep->st_phys);
	 for (i = 0; i != ep->st_count; ++ i)
	 {
		  /* PROCESS SMBIOS TABLE HERE */
		  if (i != ep->st_count - 1)
		  {
			   char *scan = (char *)header + header->length;
			   while (*(uint16_t *)scan != 0)
					++ scan;
			   header = (struct st_header_s *)(scan + 2);
		  }
	 }

	 return 0;
}
